#include "oscbb.h"
#include <limits.h>

#define sock_log(fmt, ...)  printf("[OSCBB]" fmt, ##__VA_ARGS__)


DMapParam::DMapParam()
{
    svr_addr                = 0;                /// 地址
    svr_port                = 0;                /// 端口
    svr_data_cb             = NULL;            /// 回调
    svr_data_cb_ctx         = NULL;            /// 回调用户上下文

    tcp_svr_addr                = 0;            /// tcp服务地址
    tcp_svr_port                = 0;            /// tcp服务端口
    tcp_svr_data_cb             = NULL;            /// tcp回调
    tcp_svr_data_cb_ctx         = NULL;           /// tcp回调用户上下文

    memset(inter_msg_addr, 0, sizeof(inter_msg_addr));     /// 内部通信端口,进程内部使用UNIX域UDP通信方式
    inter_msg_cb            = NULL;           /// 内部消息回调
    inter_msg_cb_ctx        = NULL;      /// 内部消息回调用户上下文

    event_trigger_cb        = NULL;       /// 触发器
    ev_trigger_ctx          = NULL;        /// 触发器用户上下文
}

tagSockEpollCtx::tagSockEpollCtx()
{
    fd          = -1;
    func        = NULL;
    context     = NULL;
}

tagSockSelectCtx::tagSockSelectCtx()
{
    fd          = -1;
    func        = NULL;
    context     = NULL;
}

namespace CBB {

/*
* socket create & bind
*/
SOCKHANDLE  cbb_sock_create(int domain, int type, int protocol, int reuse, void *addr, int addr_len)
{
    int ret;
    // create

    SOCKHANDLE fd = socket(domain, type, protocol);
    if (fd == INVALID_SOCKET)
    {
        sock_log("create socket failed,domain:%d,type:%d,protocol:%d,errno:%d\n",
                    domain, type, protocol, errno);
        return -1;
    }

    // reuse
    if (0 != reuse)
    {
        ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

        if (ret < 0)
        {
            sock_log("set sock addr reuse failed, errno:%d\n", errno);
        }
    }

    // bind
    if ((NULL != addr) && (0 != addr_len))
    {
        ret = bind(fd, (struct sockaddr *)addr, (socklen_t)addr_len);

        if (ret < 0)
        {
            sock_log("socket bind failed,errno:%d,:%d\n", errno, type);
            cbb_sock_close(fd);
            return -1;
        }
    }

    return fd;
}

SOCKHANDLE  cbb_sock_listen(int fd, int listen_num)
{
    int ret = listen(fd, listen_num);
    if (ret < 0)
    {
        sock_log("socket listen failed, errno:%d\n", errno);
        return -1;
    }
    return 0;
}

SOCKHANDLE  cbb_sock_connect(int fd, struct sockaddr *addr)
{
    int ret = connect(fd, addr, sizeof(*addr));
    if (ret < 0)
    {
        sock_log("cbb socket connect failed,errno:%d\n", errno);
        return -1;
    }
    return 0;
}

SOCKHANDLE  cbb_sock_accept(int fd, struct sockaddr *rmt_addr)
{
    unsigned int addr_len;

    addr_len = sizeof(rmt_addr);
    int client_fd = accept(fd, rmt_addr, &addr_len);
    if (client_fd  < 0)
    {
        sock_log("socket listen failed,errno:%d\n", errno);
        cbb_sock_close(client_fd);
        return -1;
    }
    return client_fd;
}

/*
 * 关闭套接字
 */
int  cbb_sock_close(SOCKHANDLE handle)
{
    return close(handle);
}

int cbb_socket_nonblock(SOCKHANDLE socket, int enable)
{
    if (enable)
      return fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) | O_NONBLOCK);
    else
      return fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) & ~O_NONBLOCK);
}


/*
 * safe send
 */
int cbb_send(SOCKHANDLE fd, void *buf, int len, int flags, bool block)
{
    int ret;
    int size     = len;
    size_t offset   = 0;

repeat:
    ret = send(fd, (char *)buf + offset, size, flags);
    if (ret < 0)
    {
        if (EINTR == errno)
        {
            goto repeat;
        }

        printf("send falied, errno:%d\n", errno);
        return -1;
    }
    else if (ret < size)
    {
        size    = size - ret;
        offset  = len - size;
        if (block)
        {
            goto repeat;
        }

        return offset;
    }
    return 0;
}

/*
 * safe send
 */
int cbb_sendto(SOCKHANDLE fd, void *buf, int len, const void *addr, int addr_len, int flags)
{
    int ret;

repeat:
    ret = sendto(fd, buf, len, 0, (const struct sockaddr *)addr, (socklen_t)addr_len);

    if (ret < 0)
    {
        if (EINTR == errno)
        {
            goto repeat;
        }

        printf("send falied,errno:%d\n", errno);
        return -1;
    }

    return 0;
}


/*
 * recv data
 */
int  cbb_recvfrom(SOCKHANDLE fd, void *buf, int buf_len, void  *addr, int *addr_len, int flags)
{
    int ret;
//    fflush(stdout);

again:
    ret = recvfrom(fd, buf, buf_len, flags, (struct sockaddr *)addr, (socklen_t *)addr_len);

    if (ret < 0)
    {
        if (EAGAIN == errno)
        {

            goto again;
        }

        printf("socket recv data failed, errno:%d\n", errno);
        return -1;
    }
    else if (0 == ret)
    {
        printf("socket recv no data, peer maybe shutdown\n");
        return -1;
    }

    return ret;
}

/*
 * recv data
 */
int cbb_recv(int fd, void *buf, int len, int flags)
{
    int ret;

again:
    ret = recv(fd, buf, len, flags);

    if (ret < 0)
    {
        if (EAGAIN == errno)
        {
            goto again;
        }

        printf("socket recv data failed, errno:%d\n", errno);
        return -1;
    }
    else if (0 == ret)
    {
        printf("socket recv no data, peer maybe shutdown\n");
        return -1;
    }

    return ret;

}

/*
 * get long int from str
 */
bool cbb_str2l(char *str, long int &val)
{
    errno = 0;    /* To distinguish success/failure after call */
    char *endptr;
    val = strtol(str, &endptr, 10);

    /* Check for various possible errors */

    if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
                || (errno != 0 && val == 0)) {
        return false;
    }

    if (endptr == str) {
        return false;
    }

    return true;

}

/*
 * miliseconds
 */
uint64_t gettime_ms(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec >> 20);
}

/*
 * local ip
 */
void get_local_ip(char *local_ip)
{
     struct ifaddrs * ifAddrStruct = NULL;
     if (local_ip == NULL)
         return;

     if (getifaddrs(&ifAddrStruct) != 0)
     {
         //if wrong, go out!
         printf("Somting is Wrong!\n");
         return ;
     }

     struct ifaddrs * iter = ifAddrStruct;

     while (iter != NULL)
     {

         if (iter->ifa_addr->sa_family == AF_INET)
         { //if ip4
             // is a valid IP4 Address
             inet_ntop(AF_INET, &((struct sockaddr_in *)iter->ifa_addr)->sin_addr, local_ip, INET_ADDRSTRLEN);
             if (((struct sockaddr_in *)iter->ifa_addr)->sin_addr.s_addr == inet_addr("127.0.0.1"))
             {

             }
             else
             {
                return;
             }
         }
         //else if (ifaddrstruct->ifa_addr->sa_family == af_inet6) { // check it is ip6
//          deal ip6 addr
         //}
         iter = iter->ifa_next;
     }

     freeifaddrs(ifAddrStruct);

     return;


}

}

